package cn.italkcloud.cph.helper;

import cn.italkcloud.cph.config.exception.BusinessException;
import cn.italkcloud.cph.constants.enums.FailedEnum;
import cn.italkcloud.cph.entity.dto.UserAuthDTO;
import cn.italkcloud.cph.utils.*;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;

import java.security.Key;
import java.util.HashMap;
import java.util.Map;

/**
 * 授权工具 采用JWTToken
 * 简化后的仅使用本地内存版本
 *
 * @author dominate
 * @since 2022/02/22
 */
public class AuthHelper {


    private static final String DEFAULT_SECRET = "6rOfFqGFe0BvL846fM79GLeZZdxoEoNcXpttXzRF1PJOm=dOE0fQjjgDuHuNU+iW";
    private static final String CACHE_USER_ID_TOKEN_FIELD_KEY = "cache:user:id:token";
    private static final String DEFAULT_HEADER_APP_TOKEN = "X-App-Token";
    private static final String DEFAULT_HEADER_USER_TOKEN = "X-User-Token";
    private static final String VALID_TIME = "time";

    /**
     * API Token验证失效时间
     */
    private static final int API_VALID_TIMEOUT = 5 * 60 * 1000;

    private static final Key JWT_SECRET_KEY;

    static {
        String loadSecret = LoadUtil.getProperty("auth.jwt-secret");
        String secret = StringUtil.isNotBlank(loadSecret) ? loadSecret : DEFAULT_SECRET;
        JWT_SECRET_KEY = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
    }


    /**
     * 设置Token无效
     *
     * @param token JWT-Token
     */
    public static void setInvalid(String token) {
        try {
            UserAuthDTO userAuth = parseWithValid(token);
            if (null != userAuth) {
                setLogout(userAuth.getAccountId());
            }
        } catch (BusinessException e) {
            // 退出登录，如果Token无效也无需处理
        }
    }

    public static boolean isValid(String token) {
        return isValid(parse(token));
    }

    public static boolean isValid(UserAuthDTO user) {
        if (null == user) {
            return false;
        }
        String token = getLoginToken(user.getAccountId());
        return user.getToken().equals(token);
    }

    public static boolean verify(String token, String id) {
        UserAuthDTO user = parse(token);
        return user.getDataPermIds().contains(id);
    }

    public static UserAuthDTO parseWithValid(String token) {
        UserAuthDTO userAuth;
        try {
            userAuth = parse(token);
        } catch (Exception e) {
            // 解析报错 没有必要记录错误的Token值
            throw BusinessException.create(FailedEnum.ERROR_TOKEN);
        }
        if (isValid(userAuth)) {
            return userAuth;
        }
        throw BusinessException.create(FailedEnum.ERROR_TOKEN);
    }

    public static String grantAuth(UserAuthDTO user) {
        // 用户数据
        // 设置随机 登录标识 Token
        user.setToken(RandomUtil.create32RandOrder(user.getAccountId()));
        // 记录登陆缓存
        setLogin(user);
        // 生成 JwtToken
        Map<String, Object> claims = BaseUtil.beanToMap(user);
        return createJwtToken(claims);
    }

    /**
     * 解析 JWT-Token 获得用户信息
     *
     * @param token JWT-Token
     * @return 用户信息
     */
    public static UserAuthDTO parse(String token) {
        Map<String, Object> claims = parseJwtToken(token);
        return BaseUtil.mapToBean(claims, UserAuthDTO.class);
    }

    /**
     * 创建API Token
     *
     * @return String
     */
    public static String createApiToken() {
        Map<String, Object> claims = new HashMap<>();
        // TODO 和SSO的AuthHelper一致即可，看情况再加点参数吧
        claims.put(VALID_TIME, System.currentTimeMillis());
        return createJwtToken(claims);
    }

    /**
     * 创建 API请求头
     *
     * @return HTTP请求头
     */
    public static Map<String, String> createApiHeader() {
        Map<String, String> headers = new HashMap<>(1);
        headers.put(DEFAULT_HEADER_APP_TOKEN, createApiToken());
        return headers;
    }

    public static String defaultApiTokenKey() {
        return DEFAULT_HEADER_APP_TOKEN;
    }

    public static String defaultUserTokenKey() {
        return DEFAULT_HEADER_USER_TOKEN;
    }

    /**
     * 是否是有效的 Api Token
     *
     * @param token 请求Token
     * @return 是否通过校验
     */
    public static boolean isValidApiToken(String token) {
        Map<String, Object> claims = parseJwtToken(token);
        if (!claims.containsKey(VALID_TIME)) {
            return false;
        }
        try {
            long time = Long.parseLong(claims.get(VALID_TIME).toString());
            return System.currentTimeMillis() - time <= API_VALID_TIMEOUT;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 解析 JWT-Token
     *
     * @param token JWT-Token
     * @return 解析结果
     */
    private static Map<String, Object> parseJwtToken(String token) {
        return Jwts.parserBuilder().setSigningKey(JWT_SECRET_KEY).build().parseClaimsJws(token).getBody();
    }

    /**
     * 创建 JWT-Token
     *
     * @param claims 加密数据
     * @return JWT-Token
     */
    private static String createJwtToken(Map<String, Object> claims) {
        return Jwts.builder().setClaims(claims).signWith(JWT_SECRET_KEY).compact();
    }

    /**
     * 设置账户退出登录
     *
     * @param accountId 退出登录的账号ID
     */
    private static void setLogout(int accountId) {
        CacheUtil.remove(CACHE_USER_ID_TOKEN_FIELD_KEY + accountId);
    }


    /**
     * 设置用户登录
     *
     * @param user 登录用户信息
     */
    private static void setLogin(UserAuthDTO user) {
        CacheUtil.set(CACHE_USER_ID_TOKEN_FIELD_KEY + user.getAccountId(), user.getToken());
    }

    /**
     * 获取用户登录标识
     *
     * @param accountId 账户ID
     * @return 登录标识
     */
    private static String getLoginToken(int accountId) {
        return CacheUtil.get(CACHE_USER_ID_TOKEN_FIELD_KEY + accountId, String.class);
    }


}
